/*!
    \copyright  Copyright (c) 2023 Qualcomm Technologies International, Ltd.\n
                All Rights Reserved.\n
                Qualcomm Technologies International, Ltd. Confidential and Proprietary.
    \file
    \brief      A demo DCF implementation demonstarating how the DCF framework can be used to advertise
                context data in a multidevice ecosystem
*/

#include "../dcf_config.h"

#ifdef INCLUDE_DCF_OPEN_GARDEN

#include "../dcf_engine.h"
#include "../dcf_core.h"
#include "bt_device.h"
#include "context_framework.h"
#include "le_advertising_manager.h"
#include "device_properties.h"
#include "local_name.h"

#include "multidevice.h"
#include "device_info.h"

#include <logging.h>
#include <string.h>

#include "state_of_charge.h"
#include "state_proxy.h"
#include "cc_with_case.h"
#include "remote_name.h"

#include <feature.h>
#include <csr_unicode.h>

#ifndef ARRAY_DIM
#define ARRAY_DIM(x) (sizeof ((x)) / sizeof ((x)[0]))
#endif

#define DEBUG_LOG_DCF_OPEN_GARDEN       DEBUG_LOG
#define DEBUG_LOG_DATA_DCF_OPEN_GARDEN  DEBUG_LOG_DATA

#define DCF_MAX_DEVICE_NAME_OCTETS  (16)

#define CASE_BATTERY_STATE_SHIFT    0x04
#define LEFT_BATTERY_STATE_SHIFT    0x02
#define RIGHT_BATTERY_STATE_SHIFT   0x00

/*! \brief Data element tags for advertising data. */
typedef enum
{
    og_data_element_device_id = 0x00,
    og_data_element_device_info = 0x20,
    og_data_element_device_address = 0x21,
    og_data_element_bt_connection_status = 0x31,
    og_data_element_device_battery = 0x35,
    og_data_element_active_audio_source = 0x38,
    og_data_element_connection_info = 0x37,
    og_data_element_earbud_status = 0x36,
    og_data_element_sound_status = 0x39,
    og_data_element_mru_info = 0x3a,
} open_garden_data_element_type_t;

/*! \brief Address Type of advertisements */
typedef enum
{
    og_address_type_public,
    og_address_type_private,
} open_garden_address_type_t;

/*! \brief Device type of peripheral */
typedef enum
{
    og_device_type_unknown,
    og_device_type_phone,
    og_device_type_laptop,
    og_device_type_watch,
    og_device_type_earbuds,
    og_device_type_ar,
    og_device_type_vr,
    og_device_type_tablet,
    og_device_type_display,
    og_device_type_tv,
    og_device_type_headset,
} open_garden_device_type_t;

/*! \brief Device Physical state */
typedef enum
{
    og_physical_status_in_case,
    og_physical_status_out_of_case,
    og_physical_status_in_ear,
    og_physical_status_on_head,
} open_garden_device_physical_status_t;

/*! \brief ANC State */
typedef enum
{
    og_anc_state_anc_not_supported,
    og_anc_state_anc_on,
    og_anc_state_anc_off,
} open_garden_anc_state_t;

/*! \brief ANC State */
typedef enum
{
    og_snapdragon_sound_not_supported,
    og_snapdragon_sound_on,
    og_snapdragon_sound_off,
    og_snapdragon_sound_unknown,
} open_garden_snapdragon_sound_t;


typedef enum
{
    battery_state_charged,
    battery_state_charging,
    battery_state_discharging,
    battery_state_unknown,
} battery_state_t;


/*! \brief Active audio source */
typedef enum
{
    og_active_source_type_none,
    og_active_source_type_media,
    og_active_source_type_voice,
    og_active_source_type_phone_call,
} open_garden_active_source_type_t;

#define OPEN_GARDEN_HEADER_VERSION  DCF_V1_HEADER_VERSION
#define OPEN_GARDEN_UUID    0xFCC2

/*! \brief Max data element set size */
#define OPEN_GARDEN_DATA_ELEMENT_SET_SIZE_MAX   251
/*! \brief Max advertising header size */
#define DCF_ADVERTISING_HEADER_SIZE             4
/*! \brief Max Advertisement size */
#define OPEN_GARDEN_ADVERTISING_SIZE_MAX        (DCF_ADVERTISING_HEADER_SIZE + OPEN_GARDEN_DATA_ELEMENT_SET_SIZE_MAX)

/*! \brief The advertising header array*/
static const int8 advertising_header[DCF_ADVERTISING_HEADER_SIZE] =
{
    0, // to be populated based on data elements being advertised
    ble_ad_type_service_data,
    OPEN_GARDEN_UUID & 0xFF,
    (OPEN_GARDEN_UUID >> 8) & 0xFF,
};


/*!
*   \brief  Message handler for when the State Proxy has an update, invoking this function.
*
*   \param  Task    The task registered for event updates. (unused)
*   \param  id      The message ID containing enum value to be handled in function.
*   \param  message The message itself. (unused)
*/
static void dcfOpenGarden_HandleStateProxyUpdate(Task task, MessageId id, Message message);

static void dcfOpenGarden_HandleContextUpdate(Task task, MessageId id, Message message);

static void dcfOpenGarden_HandleCaseUpdate(Task task, MessageId id, Message message);

/*! \brief TaskData Handler functions for Context Updates */
static TaskData contextUpdateHandler = { .handler = dcfOpenGarden_HandleContextUpdate };

/*! \brief TaskData Handler functions for State Proxy Updates */
static TaskData stateProxyUpdateHandler = { .handler = dcfOpenGarden_HandleStateProxyUpdate };

static TaskData caseCommsUpdateHandler = { .handler = dcfOpenGarden_HandleCaseUpdate };
/*
*   Data element constructors.
*
*   data            Pointer to the data array, elements added to data through pointer.
*   max_data_size   The max size for data to be added to the array.
*
*   Returns the running total for index offset (Number of bytes added to data).
*/
static uint8 dcfOpenGarden_ConstructDeviceId(uint8 * data, uint8 max_data_size);
static uint8 dcfOpenGarden_ConstructDeviceInfo(uint8 * data, uint8 max_data_size);
static uint8 dcfOpenGarden_ConstructDeviceAddress(uint8 * data, uint8 max_data_size);
static uint8 dcfOpenGarden_ConstructActiveAudioSource(uint8 * data, uint8 max_data_size);
static uint8 dcfOpenGarden_ConstructConnectionInfo(uint8 * data, uint8 max_data_size);
static uint8 dcfOpenGarden_ConstructMruInfo(uint8 * data, uint8 max_data_size);
static uint8 dcfOpenGarden_ConstructBatteryInfo(uint8 * data, uint8 max_data_size);
static uint8 dcfOpenGarden_ConstructEarbudStatus(uint8 * data, uint8 max_data_size);
static uint8 dcfOpenGarden_ConstructSoundStatus(uint8 * data, uint8 max_data_size);
static uint8 dcfOpenGarden_ConstructHandsetConnectionInfo(uint8 * data, uint8 max_data_size);

static const dcf_data_element_constructor_t de_constructors[] =
{
    { .type = og_data_element_device_id, .constructor_function = dcfOpenGarden_ConstructDeviceId },
    { .type = og_data_element_device_info, .constructor_function = dcfOpenGarden_ConstructDeviceInfo },
    { .type = og_data_element_device_address, .constructor_function = dcfOpenGarden_ConstructDeviceAddress },
    { .type = og_data_element_active_audio_source, .constructor_function = dcfOpenGarden_ConstructActiveAudioSource },
    { .type = og_data_element_connection_info, .constructor_function = dcfOpenGarden_ConstructConnectionInfo },
    { .type = og_data_element_mru_info, .constructor_function = dcfOpenGarden_ConstructMruInfo },
    { .type = og_data_element_device_battery, .constructor_function = dcfOpenGarden_ConstructBatteryInfo },
    { .type = og_data_element_earbud_status, .constructor_function = dcfOpenGarden_ConstructEarbudStatus  },
    { .type = og_data_element_sound_status, .constructor_function = dcfOpenGarden_ConstructSoundStatus },
    { .type = og_data_element_bt_connection_status, .constructor_function = dcfOpenGarden_ConstructHandsetConnectionInfo }
};

static const dcf_data_element_set_t open_garden_data_element_set =
{
    .constructors = (dcf_data_element_constructor_t *)de_constructors,
    .number_of_constructors = ARRAY_DIM(de_constructors),
    .identity = dcf_v1_public_identity
};

static le_adv_item_handle registered_advertising_handle = NULL;
static uint8 advertising_data[OPEN_GARDEN_ADVERTISING_SIZE_MAX] = { 0 };
static unsigned advertising_data_size = 0;
static uint8 cached_case_battery_level = 0x7F; // Unknown unless overwritten with cached value

static inline uint8 dcfOpenGarden_GetDeviceType(void)
{
    return ((Multidevice_GetType() == multidevice_type_pair) ? og_device_type_earbuds : og_device_type_headset);
}

static uint8 dcfOpenGarden_ConstructSoundStatus(uint8 * data, uint8 max_data_size)
{
    PanicFalse(4 <= max_data_size);
    PanicNull(data);
    unsigned index = 0;

    if(StateProxy_GetLocalAncState() || StateProxy_GetPeerAncState() || StateProxy_GetPeerAancState() || StateProxy_GetLocalAancState())
    {
        data[index++] = og_anc_state_anc_on;
    }
    else
    {
        data[index++] = og_anc_state_anc_off;
    }

    data[index++] = og_snapdragon_sound_on;

    context_active_source_volume_info_t audio_source_volume;
    if(ContextFramework_GetContextItem(context_audio_volume_info, (unsigned *)&audio_source_volume, sizeof(context_active_source_volume_info_t)))
    {
        data[index++] = audio_source_volume.active_source_volume_percent;
    }

    DEBUG_LOG_DCF_OPEN_GARDEN("dcfOpenGarden_ConstructSoundStatus length=%d", index);
    DEBUG_LOG_DATA_DCF_OPEN_GARDEN(data, index);

    return index;
}

static uint8 dcfOpenGarden_ConstructEarbudStatus(uint8 * data, uint8 max_data_size)
{
    PanicFalse(2 <= max_data_size);
    PanicNull(data);
    unsigned index = 0;

    uint8 open_garden_device_info = og_physical_status_out_of_case;
    context_physical_state_t physical_state;
    if(ContextFramework_GetContextItem(context_physical_state, (unsigned *)&physical_state, sizeof(context_physical_state_t)))
    {
        switch(physical_state)
        {
            case in_case:
                open_garden_device_info = og_physical_status_in_case;
                break;
            case out_of_case:
                open_garden_device_info = og_physical_status_out_of_case;
                break;
            case on_head:
                open_garden_device_info = og_physical_status_in_ear;
                break;
            default:
                break;
        }
    }

    data[index++] = open_garden_device_info;

    DEBUG_LOG_DCF_OPEN_GARDEN("dcfOpenGarden_ConstructEarbudStatus length=%d", index);
    DEBUG_LOG_DATA_DCF_OPEN_GARDEN(data, index);

    return index;
}

static inline bool dcfOpenGarden_RemoteNameIsAvaliable(device_t handset)
{
    return handset && RemoteName_IsAvailable(handset);
}

static uint8 dcfOpenGarden_ConstructHandsetConnectionInfo(uint8 * data, uint8 max_data_size)
{
    unsigned index = 0;
    context_connected_handsets_info_t connected_handsets_info;

    if(ContextFramework_GetContextItem(context_connected_handsets_info, (unsigned *)&connected_handsets_info, sizeof(context_connected_handsets_info_t)))
    {
        PanicNull(data);
        PanicFalse(max_data_size >= 1);

        data[index++] = connected_handsets_info.number_of_connected_handsets;
        if (connected_handsets_info.number_of_connected_handsets)
        {
            device_t handset = connected_handsets_info.connected_handsets[0].handset;

            if(dcfOpenGarden_RemoteNameIsAvaliable(handset))
            {
                uint16 handset_name_size;
                const char *handset_name = RemoteName_Get(handset, &handset_name_size);
        
                /* discount string terminator(s) */
                handset_name_size = strlen(handset_name);

                if (handset_name_size > DCF_MAX_DEVICE_NAME_OCTETS)
                {
                    CsrUtf8StrTruncate((uint8 *) handset_name, DCF_MAX_DEVICE_NAME_OCTETS);
                    handset_name_size = strlen(handset_name);
                }

                PanicFalse(1 + handset_name_size <= max_data_size);

                data[index++] = handset_name_size;
                memcpy(&data[index], handset_name, handset_name_size);

                free((void*)handset_name);
                handset_name = NULL;
            }
            else
            {
                PanicFalse(max_data_size >= 2);
                data[index++] = 0;
            }
        }
    }

    DEBUG_LOG_DCF_OPEN_GARDEN("dcfOpenGarden_ConstructHandsetConnectionInfo length=%d", index);
    DEBUG_LOG_DATA_DCF_OPEN_GARDEN(data, index);

    return index;
}

static uint8 dcfOpenGarden_ConstructMruInfo(uint8 * data, uint8 max_data_size)
{
   PanicFalse(6 <= max_data_size);
   PanicNull(data);
   unsigned index = 0;

   static tp_bdaddr mru_addr = { 0 };
    
   device_t mru_device = BtDevice_GetMruDevice();
   
   BtDevice_GetTpBdaddrForDevice(mru_device, &mru_addr);

   data[index++] = (mru_addr.taddr.addr.nap >> 8);
   data[index++] = mru_addr.taddr.addr.nap;
   data[index++] = mru_addr.taddr.addr.uap;
   data[index++] = (mru_addr.taddr.addr.lap >> 16);
   data[index++] = (mru_addr.taddr.addr.lap >> 8);
   data[index++] = mru_addr.taddr.addr.lap;

   DEBUG_LOG_DCF_OPEN_GARDEN("dcfOpenGarden_ConstructMruInfo length=%d", index);
   DEBUG_LOG_DATA_DCF_OPEN_GARDEN(data, index);
   return index;
}

static battery_state_t dcfOpenGarden_GetCaseBatteryState(void)
{

    if((CcWithCase_GetCaseBatteryState() & 0x7F) >= 0x64)
    {
        return battery_state_charged;
    }
    else if(CcWithCase_IsCaseChargerConnected())
    {
        return battery_state_charging;
    }
    else if(CcWithCase_GetCaseBatteryState() == 0x7F)
    {
        return battery_state_unknown;
    }
    else
    {
        return battery_state_discharging;
    }
}

static inline battery_state_t dcfOpenGarden_GetLeftBatteryState(void)
{
    bool is_in_case = (Multidevice_IsLeft() ? StateProxy_IsInCase() : StateProxy_IsPeerInCase());
    return (is_in_case ? battery_state_charging : battery_state_discharging);
}

static inline battery_state_t dcfOpenGarden_GetRightBatteryState(void)
{
    bool is_in_case = ((!Multidevice_IsLeft()) ? StateProxy_IsInCase() : StateProxy_IsPeerInCase());
    return (is_in_case ? battery_state_charging : battery_state_discharging);
}

static inline bool dcfOpenGarden_IsBatteryLevelValid(uint8 battery_level)
{
    return ((battery_level > 0 && battery_level <= 100) ? TRUE : FALSE);
}

static inline void dcfOpenGarden_CacheCaseBatteryLevel(uint8 battery_level)
{
    cached_case_battery_level = battery_level;
}

static inline uint8 dcfOpenGarden_GetCaseBatteryLevel(void)
{
    uint8 case_battery_level = CcWithCase_GetCaseBatteryState() & 0x7F;

    if (dcfOpenGarden_IsBatteryLevelValid(case_battery_level))
    {
        dcfOpenGarden_CacheCaseBatteryLevel(case_battery_level);
    }
    
    return cached_case_battery_level;
}

static inline uint8 dcfOpenGarden_GetLeftBatteryLevel(void)
{
    uint16 battery_level_local;
    uint16 battery_level_peer;
    StateProxy_GetLocalAndRemoteBatteryLevels(&battery_level_local, &battery_level_peer);
    return (Multidevice_IsLeft() ? Soc_ConvertLevelToPercentage(battery_level_local) : Soc_ConvertLevelToPercentage(battery_level_peer));
}

static inline uint8 dcfOpenGarden_GetRightBatteryLevel(void)
{
    uint16 battery_level_local;
    uint16 battery_level_peer;
    StateProxy_GetLocalAndRemoteBatteryLevels(&battery_level_local, &battery_level_peer);
    return ((!Multidevice_IsLeft()) ? Soc_ConvertLevelToPercentage(battery_level_local) : Soc_ConvertLevelToPercentage(battery_level_peer));
}

static uint8 dcfOpenGarden_ConstructBatteryInfo(uint8 * data, uint8 max_data_size)
{
    PanicFalse(4 <= max_data_size);
    PanicNull(data);
    unsigned index = 0;

    uint8 battery_state = ((dcfOpenGarden_GetCaseBatteryState() << CASE_BATTERY_STATE_SHIFT)
                            | (dcfOpenGarden_GetLeftBatteryState() << LEFT_BATTERY_STATE_SHIFT)
                            | (dcfOpenGarden_GetRightBatteryState() << RIGHT_BATTERY_STATE_SHIFT));

    data[index++] = battery_state;
    data[index++] = dcfOpenGarden_GetCaseBatteryLevel();
    data[index++] = dcfOpenGarden_GetLeftBatteryLevel();
    data[index++] = dcfOpenGarden_GetRightBatteryLevel();

    DEBUG_LOG_DCF_OPEN_GARDEN("dcfOpenGarden_ConstructBatteryInfo length=%d", index);
    DEBUG_LOG_DATA_DCF_OPEN_GARDEN(data, index);
    return index;
}

static uint8 dcfOpenGarden_ConstructDeviceId(uint8 * data, uint8 max_data_size)
{
    PanicFalse(6 <= max_data_size);
    bdaddr bd_addr = { 0 };
    appDeviceGetPrimaryBdAddr(&bd_addr);
    unsigned index = 0;
    data[index++] = (bd_addr.nap >> 8);
    data[index++] = bd_addr.nap;
    data[index++] = bd_addr.uap;
    data[index++] = (bd_addr.lap >> 16);
    data[index++] = (bd_addr.lap >> 8);
    data[index++] = bd_addr.lap;
    DEBUG_LOG_DCF_OPEN_GARDEN("dcfOpenGarden_ConstructDeviceId length=%d", index);
    DEBUG_LOG_DATA_DCF_OPEN_GARDEN(data, index);
    return index;
}

static uint8 dcfOpenGarden_ConstructDeviceInfo(uint8 * data, uint8 max_data_size)
{
    unsigned index = 0;
    uint16 name_length = 0;
    const uint8 * name = LocalName_GetName(&name_length);

    const char * manufacturer_name =  DeviceInfo_GetManufacturer();
    size_t manufacturer_name_length = strlen(manufacturer_name);

    const char * model_name =  DeviceInfo_GetModelId();
    size_t model_name_length = strlen(model_name);

    uint8 total_length = (name_length + manufacturer_name_length + model_name_length + 10);

    PanicFalse(manufacturer_name_length <= 16);
    PanicFalse(model_name_length <= 16);
    PanicFalse(total_length <= max_data_size);

    data[index++] = dcfOpenGarden_GetDeviceType();

    data[index++] = manufacturer_name_length;
    memcpy(&data[index], manufacturer_name, manufacturer_name_length);
    index += manufacturer_name_length;

    data[index++] = model_name_length;
    memcpy(&data[index], model_name, model_name_length);
    index += model_name_length;

    data[index++] = name_length;
    memcpy(&data[index], name, name_length);
    index += name_length;

    DEBUG_LOG_DCF_OPEN_GARDEN("dcfOpenGarden_ConstructDeviceInfo length=%d", total_length);
    DEBUG_LOG_DATA_DCF_OPEN_GARDEN(data, total_length);
    return index;
}

static uint8 dcfOpenGarden_ConstructDeviceAddress(uint8 * data, uint8 max_data_size)
{
    PanicNull(data);
    PanicFalse(6 <= max_data_size);
    bdaddr bd_addr = { 0 };
    appDeviceGetPrimaryBdAddr(&bd_addr);
    unsigned index = 0;
    data[index++] = (bd_addr.nap >> 8);
    data[index++] = bd_addr.nap;
    data[index++] = bd_addr.uap;
    data[index++] = (bd_addr.lap >> 16);
    data[index++] = (bd_addr.lap >> 8);
    data[index++] = bd_addr.lap;
    DEBUG_LOG_DCF_OPEN_GARDEN("dcfOpenGarden_ConstructDeviceAddress length=%d", index);
    DEBUG_LOG_DATA_DCF_OPEN_GARDEN(data, index);
    return index;
}

static uint8 dcfOpenGarden_ConstructActiveAudioSource(uint8 * data, uint8 max_data_size)
{
    PanicNull(data);
    PanicFalse(1 <= max_data_size);
    context_active_source_info_t active_source_info;
    open_garden_active_source_type_t active_source_type = og_active_source_type_none;
    if(ContextFramework_GetContextItem(context_active_source_info, (unsigned *)&active_source_info, sizeof(context_active_source_info_t)))
    {
        if(active_source_info.active_source.type == source_type_voice)
        {
            active_source_type = og_active_source_type_phone_call;
        }
        else if(active_source_info.active_source.type == source_type_audio)
        {
            active_source_type = og_active_source_type_media;
        }
    }
    data[0] = active_source_type;
    DEBUG_LOG_DCF_OPEN_GARDEN("dcfOpenGarden_ConstructActiveAudioSource length=%d data=0x%x", 1, data[0]);
    return 1;
}

static uint8 dcfOpenGarden_ConstructConnectionInfo(uint8 * data, uint8 max_data_size)
{
    uint8 index = 0;
    context_connected_handsets_info_t connected_handsets_info = { .number_of_connected_handsets = 0, .connected_handsets = { 0 } };
    if(ContextFramework_GetContextItem(context_connected_handsets_info, (unsigned *)&connected_handsets_info, sizeof(context_connected_handsets_info_t)))
    {
        PanicNull(data);
        PanicFalse((1 + (connected_handsets_info.number_of_connected_handsets * sizeof(bdaddr))) <= max_data_size);
        data[index++] = connected_handsets_info.number_of_connected_handsets;
        for(unsigned i = 0; i < connected_handsets_info.number_of_connected_handsets; i++)
        {
            tp_bdaddr connected_address = { 0 };
            if(BtDevice_GetTpBdaddrForDevice(connected_handsets_info.connected_handsets[i].handset, &connected_address))
            {
                data[index++] = (connected_address.taddr.addr.nap >> 8);
                data[index++] = connected_address.taddr.addr.nap;
                data[index++] = connected_address.taddr.addr.uap;
                data[index++] = (connected_address.taddr.addr.lap >> 16);
                data[index++] = (connected_address.taddr.addr.lap >> 8);
                data[index++] = connected_address.taddr.addr.lap;
            }
        }
        DEBUG_LOG_DCF_OPEN_GARDEN("dcfOpenGarden_ConstructConnectionInfo length=%d", index);
        DEBUG_LOG_DATA_DCF_OPEN_GARDEN(data, index);
    }
    return index;
}

static uint8 dcfOpenGarden_ConstructAdvertisingHeader(uint8 * data, uint8 max_data_size)
{
    PanicNull(data);
    PanicFalse(DCF_ADVERTISING_HEADER_SIZE <= max_data_size);
    memcpy(data, advertising_header, DCF_ADVERTISING_HEADER_SIZE);
    DEBUG_LOG_DCF_OPEN_GARDEN("dcfOpenGarden_ConstructAdvertisingHeader length=%d", DCF_ADVERTISING_HEADER_SIZE);
    DEBUG_LOG_DATA_DCF_OPEN_GARDEN(data, DCF_ADVERTISING_HEADER_SIZE);
    return DCF_ADVERTISING_HEADER_SIZE;
}

static void dcfOpenGarden_PopulateDataSizeInHeader(uint8 * data, uint8 advertising_length)
{
    DEBUG_LOG_DCF_OPEN_GARDEN("dcfOpenGarden_PopulateDataSizeInHeader length=%d", (advertising_length-1));
    data[0] = (advertising_length - 1);
}

static void dcfOopenGarden_ConstructAdvertisingData(void)
{
    memset(advertising_data, 0, OPEN_GARDEN_ADVERTISING_SIZE_MAX);
    unsigned index = 0;
    index += dcfOpenGarden_ConstructAdvertisingHeader(&advertising_data[index], OPEN_GARDEN_ADVERTISING_SIZE_MAX);

    index += Dcfv1format_ConstructDataElementSet(&advertising_data[index], (OPEN_GARDEN_ADVERTISING_SIZE_MAX-index), &open_garden_data_element_set);
    advertising_data_size = index;

    dcfOpenGarden_PopulateDataSizeInHeader(advertising_data, advertising_data_size);

    DEBUG_LOG_DCF_OPEN_GARDEN("dcfOopenGarden_ConstructAdvertisingData length=%d", advertising_data_size);
    DEBUG_LOG_DATA_DCF_OPEN_GARDEN(advertising_data, advertising_data_size);

    if(registered_advertising_handle)
    {
        LeAdvertisingManager_UpdateAdvertisingItem(registered_advertising_handle);
    }
}

static void dcfOpenGarden_HandleStateProxyUpdate(Task task, MessageId id, Message message)
{
    UNUSED(task);
    UNUSED(message);

    switch(id)
    {
        case STATE_PROXY_EVENT:
        case state_proxy_event_type_battery_voltage:
        case state_proxy_event_type_anc:
            dcfOopenGarden_ConstructAdvertisingData();
            break;
        default:
            Panic();
            break;
    }
}

static void dcfOpenGarden_HandleContextUpdate(Task task, MessageId id, Message message)
{
    UNUSED(task);
    UNUSED(message);

    switch(id)
    {
        case context_physical_state:
        case context_active_source_info:
        case context_connected_handsets_info:
        case context_audio_volume_info:
            dcfOopenGarden_ConstructAdvertisingData();
            break;
        default:
            Panic();
            break;
    }
}

static void dcfOpenGarden_HandleCaseUpdate(Task task, MessageId id, Message message)
{
    UNUSED(task);
    UNUSED(message);

    switch(id)
    {
        case CASE_POWER_STATE:
        case CASE_LID_STATE:
            dcfOopenGarden_ConstructAdvertisingData();
            break;
        default:
            Panic();
            break;
    }
}

static uint8 dcfOpenGarden_GetItemDataSize(void)
{
    if(advertising_data_size == 0)
    {
        dcfOopenGarden_ConstructAdvertisingData();
    }
    return advertising_data_size;
}

static bool dcfOpenGarden_GetItemData(le_adv_item_data_t * data)
{
    PanicNull(data);
    data->size = dcfOpenGarden_GetItemDataSize();
    data->data = advertising_data;
    return TRUE;
}

static bool dcfOpenGarden_GetItemInfo(le_adv_item_info_t * info)
{
    PanicNull(info);
    *info = (le_adv_item_info_t){ .placement = le_adv_item_data_placement_advert,
                                        .type = le_adv_type_extended_connectable,
                                        .data_size = dcfOpenGarden_GetItemDataSize() };
    return TRUE;
}

static const le_adv_item_callback_t dcfOpenGarden_advertising_callbacks =
{
    .GetItemData = &dcfOpenGarden_GetItemData,
    .GetItemInfo = &dcfOpenGarden_GetItemInfo,
};


void DcfOpenGarden_Init(void)
{
    ContextFramework_RegisterContextConsumer(context_physical_state, &contextUpdateHandler);
    ContextFramework_RegisterContextConsumer(context_active_source_info, &contextUpdateHandler);
    ContextFramework_RegisterContextConsumer(context_connected_handsets_info, &contextUpdateHandler);
    ContextFramework_RegisterContextConsumer(context_audio_volume_info, &contextUpdateHandler);

    CcWithCase_RegisterStateClient(&caseCommsUpdateHandler);

    StateProxy_EventRegisterClient(&stateProxyUpdateHandler, state_proxy_event_type_battery_voltage);
    StateProxy_EventRegisterClient(&stateProxyUpdateHandler, state_proxy_event_type_anc);
    StateProxy_EventRegisterClient(&stateProxyUpdateHandler, state_proxy_event_type_aanc);

    TaskData init_task;

    RemoteName_Init(&init_task);

    registered_advertising_handle = LeAdvertisingManager_RegisterAdvertisingItemCallback(NULL, &dcfOpenGarden_advertising_callbacks);
}

#endif
